package figure8;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.UUID;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Control;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

import figure8.GUI.EffectPanel;
import figure8.GUI.TrackPanel;
import figure8.GUI.WAVTrackPanel;
import figure8.GUI.EffectWindow.Effect;
import figure8.Network.Client;
import figure8.Network.Message;
import figure8.Network.NetworkConnection;

public class WAVTrack extends BaseTrack{
	protected static Type type = Type.WAV;
	
	private Clip clip;
	private File origFile = null;
//	private File effFile = null;
	private String effChain = "x";
	static int debug = 0;
	
	private LoopTiming timing;
	private Control[] Ctrls;
	private DataLine dataLine;
	private static final long serialVersionUID = -1219322788922037434L;

	protected int frame;
	
	public static WAVTrack fromString(Project project, String serialized) {
		WAVTrack track = null;
		try {
			File file = new File(project.getLocation().getAbsoluteFile(), "Recordings/" + UUID.randomUUID() + ".wav");
			file.createNewFile();
			
			track = new WAVTrack((Clip) Rec.mixerPlay.getLine(Rec.info), file, project.getTiming(), false);
		} catch (LineUnavailableException e) {
			e.printStackTrace();
			System.err.println("Couldn't get mixer line :(");
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("Couldn't create blank file :(");
		} catch (UnsupportedAudioFileException e) {
			e.printStackTrace();
		}
		track.deserializeMetadata(serialized);
		
		return track;
	}
	
	// This constructor is for making a WAV track that doesn't yet a file, for
	// networking only!
	public WAVTrack(DataLine line, File file, LoopTiming timing, boolean ready) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
		super("Unnamed WAVTrack " + (debug++), false, false, 0, 0);
		this.file = file;
		this.origFile = file;
		this.frame = 0;
		dataLine = line;
		this.timing = timing;
		clip = (Clip) line;
		Ctrls = clip.getControls();
	}
	
	@Override
	public void finalize() {
		super.finalize();
		
		try {
			clip.open(AudioSystem.getAudioInputStream(file));
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	// ---
	public WAVTrack(DataLine line, File file, LoopTiming timing) throws LineUnavailableException, IOException, UnsupportedAudioFileException {
		super("Unnamed WAVTrack " + (debug++), false, false, 0, 0);

		this.origFile = new File(file.getName().replace(".wav", "orig.wav"));
		this.file = file;
		this.origFile = file;

		this.frame = 0;
		
		dataLine = line;
		this.timing = timing;

		clip = (Clip) line;
		
		clip.open(AudioSystem.getAudioInputStream(file));
		Ctrls = clip.getControls();
	}
	
	public String updateEffFile(ArrayList<EffectPanel> effPanels) 
	throws LineUnavailableException, IOException, UnsupportedAudioFileException, WavFileException {
		setFile(getOrigFile());
		if(effPanels.isEmpty()){ return "x"; }
		else {
			String totEffects = "";
			for(EffectPanel eff : effPanels) {
				
				SignalAlteration alteredFile = 
					new SignalAlteration(getFile(), eff.getEffect(), eff.getSettings());
				setFile(alteredFile.returnFile());
				//Adds to the effect chain String.
				//every effect is split by a ';' and 
				//every element of that effect by ':'
			    totEffects = totEffects + eff.getEffect().toString();
			    for(double setting : eff.getSettings())
			    	totEffects = totEffects + ":" + String.valueOf(setting);
			    totEffects = totEffects + ";";
			}
			return totEffects;
		}
	}
	
	@Override 
	public File getFile() {
		return this.origFile;
	}
	
	@Override
	public void play() {
		this.clip.setFramePosition(this.frame);
		this.clip.start();
	}
	
	public void reset () {
		this.clip.setFramePosition(0);
	}
	
	@Override
	public void pause() {
		this.frame = this.clip.getFramePosition();
		this.clip.stop();
	}

	@Override
	public void stop() {
		this.frame = 0;
		this.clip.stop();
	}

	public LoopTiming getTiming() {	return timing; }

	public boolean isWAV() { return true; }

	public FloatControl getVolCont() {
		FloatControl getVol = null;
		try {
			getVol = (FloatControl) dataLine
					.getControl(FloatControl.Type.MASTER_GAIN);
		} catch (Exception e) {
			System.out.println("Failed trying to get VOLUME control: " + e);
		}
		if (getVol == null)
			System.out.println("No volume");
		return getVol;
	}
	
	public FloatControl getPanCont() {
		FloatControl getPan = null;
		try {
			getPan = (FloatControl) dataLine
					.getControl(FloatControl.Type.PAN);
		} catch (Exception e) {
			System.out.println("Failed trying to get PAN control: " + e);
		}
		if (getPan == null)
			System.out.println("No PAN");
		return getPan;
	}
	
	@Override
	public TrackPanel makeTrackPanel(Controller controller) {
		return new WAVTrackPanel(controller, this);
	}

	@Override
	public void setMuted(boolean muted) {
		super.setMuted(muted);
		
		BooleanControl muteControl = (BooleanControl) dataLine
				.getControl(BooleanControl.Type.MUTE);
		muteControl.setValue(muted);
	}

	@Override
	public void setVolume(float volume) {
		super.setVolume(volume);
		this.getVolCont().setValue(volume);
	}
	
	@Override
	public void setPan(float pan) {
		super.setPan(pan);
		this.getPanCont().setValue(pan);
	}

	@Override
	public Boolean[][] getDrumMachine() { return null; }
	
	@Override
	public void close() {
	    clip.stop();
		setMuted(true);
		this.clip.setFramePosition(this.clip.getFrameLength() - 1);
		clip.drain();
		clip.close();
	}
	
	public Clip getClip() { return clip; }
	
	public File getOrigFile() { return origFile; }
	
	public void setFile(File newFile) throws LineUnavailableException, IOException, UnsupportedAudioFileException {	
		this.file = newFile;
		clip.close();
		clip.open(AudioSystem.getAudioInputStream(file));
	}
	
	public void setEffChain(String s) { effChain = s; }
	
	public String[] getEffChain() {	return effChain.split(";");	}
	public String getEffChainStr() {  System.out.println(this.getName());return effChain;}
	
	public DataLine getLine() {	return dataLine; }
	
	@Override
	public void send(NetworkConnection client, Client exclude) {
		System.out.println("Sending WAVTrack " + this.getName());
		
		String data = "WAV " + this.serializeMetadata();
		
		client.send(new Message(Message.Type.TRACK_ADD, data), exclude);
		
		this.sendFile(client, exclude);
	}
	
    @Override
    public String serializeMetadata() {
        return super.serializeMetadata() + " " + this.file.length();
    }

    public String deserializeMetadata(String metadata) {
        String[] parts = super.deserializeMetadata(metadata).split(" ", 2);
        
        this.size = Integer.parseInt(parts[0]);
        
        String leftover;
        
        if (parts.length < 2) {
                leftover = "";
        } else {
                leftover = parts[3];
        }
        
        return leftover;
    }
    
    @Override
	public void updateMeta(String data) {
		this.deserializeMetadata(data);
		this.fireTrackChangedEvent(false);
	}
}
